package com.yungu.swift.admin.base;

import com.alibaba.dubbo.rpc.*;
import com.google.common.collect.Sets;
import com.yungu.swift.base.ResponseData;
import com.yungu.swift.base.model.BaseDto;
import com.yungu.swift.base.model.BaseVo;
import com.yungu.swift.base.model.PageVo;
import com.yungu.swift.base.model.admin.AdminParam;
import com.yungu.swift.base.model.annotation.Desensitization;
import com.yungu.swift.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author : cuixiuyin
 * @date : 2019/11/19
 */
@Slf4j
public class SensitiveFilter implements Filter {

    private static final Set<String> ALLOW_ROLE = Sets.newHashSet("4b7678621bfd4c1991144e1c707956b0",
            "c7750eea6d574f55915af59a1219b9a5", "bae2c29b1b3444349c73173d64790cd0");

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        log.debug("before filter");
        Object userRoleUuid = null;
        Object[] arguments = invocation.getArguments();
        for (Object obj : arguments) {
            if (obj != null && AdminParam.class.isAssignableFrom(obj.getClass())) {
                userRoleUuid = ((AdminParam) obj).getUserRoleUuid();
                break;
            }
            if (obj != null && Map.class.isAssignableFrom(obj.getClass())) {
                Map map = (Map) obj;
                userRoleUuid = map.get("userRoleUuid");
            }
        }
        Result result = invoker.invoke(invocation);
        if (result.getException() instanceof Exception) {

        } else {
            //如果不是内置角色，则进行脱敏处理处理
            if (userRoleUuid == null || ALLOW_ROLE.contains(userRoleUuid)) {
                return result;
            }
            Object body = result.getValue();
            if (body instanceof ResponseData) {
                Object data = ((ResponseData) body).getData();
                if (data == null) {
                    return result;
                }
                if (data instanceof PageVo) {
                    data = ((PageVo) data).getItems();
                }
                if (data instanceof Collection && CollectionUtils.isNotEmpty((Collection<Object>) data)) {
                    handleCollection((Collection<Object>) data);
                    return result;
                }
                if (data instanceof Map) {
                    Collection values = ((Map) data).values();
                    handleCollection(values);
                    return result;
                }
                handle(data);
            }
        }
        return result;
    }

    private void handleCollection(Collection<Object> objects) {
        for (Object obj : objects) {
            handle(obj);
        }
    }

    private void handle(Object obj) {
        if (obj == null) {
            return;
        }
        Class<?> aClass = obj.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            try {
                field.setAccessible(true);
                Object fieldObj = field.get(obj);
                if (fieldObj == null) {
                    continue;
                }
                // 判断非基本数据类型，是 Dto/Vo 的情况
                if (BaseDto.class.isAssignableFrom(field.getType()) || BaseVo.class.isAssignableFrom(field.getType())) {
                    handle(fieldObj);
                }
                // 判断 PageVo 情况
                if (PageVo.class.isAssignableFrom(field.getType())) {
                    PageVo pageVo = (PageVo) fieldObj;
                    handleCollection(pageVo.getItems());
                }
                // 判断 Collection 类型
                if (Collection.class.isAssignableFrom(field.getType())) {
                    handleCollection((Collection<Object>) fieldObj);
                }
                // 判断 Map 类型
                if (Map.class.isAssignableFrom(field.getType())) {
                    Map fieldObjMap = (Map) fieldObj;
                    handleCollection(fieldObjMap.values());
                }
                handleField(field, obj);
            } catch (IllegalAccessException e) {
                log.error("SensitiveFilter Class Method invoke IllegalAccessException fieldName:{}.", field.getName(), e);
            }
        }
    }


    private void handleField(Field field, Object obj) throws IllegalAccessException {
        Desensitization desensitization = field.getAnnotation(Desensitization.class);
        // 针对子帐号进行脱敏操作
        if (desensitization != null && field.get(obj) != null) {
            List<String> regular = null;
            Desensitization.DesensitionType type = desensitization.type();
            switch (type) {
                case CUSTOM:
                    regular = Arrays.asList(desensitization.attach());
                    break;
                case NAME:
                    desensitizationName(field, obj);
                    break;
                case ADDRESS:
                    desensitizationAddress(field, obj);
                    break;
                default:
                    regular = Arrays.asList(type.getRegular());
                    break;
            }
            if (CollectionUtils.isNotEmpty(regular)) {
                String match = regular.get(0);
                String result = regular.get(1);
                if (StringUtils.isNotEmpty(match) && StringUtils.isNotEmpty(result)) {
                    field.setAccessible(true);
                    field.set(obj, String.valueOf(field.get(obj)).replaceAll(match, result));
                }
            }
        }
    }

    /**
     * 对地址属性进行手动脱敏处理
     *
     * @param field
     * @param obj
     */
    private void desensitizationAddress(Field field, Object obj) throws IllegalAccessException {
        Object value = field.get(obj);
        if (value != null) {
            String name = (String) value;
            String result = StringUtils.left(name, 3);
            for (int i = 0; i < name.length() - 3; i++) {
                result += "*";
            }
            field.setAccessible(true);
            field.set(obj, result);
        }
    }

    /**
     * 对名字属性进行手动脱敏处理
     *
     * @param field
     * @param obj
     */
    private void desensitizationName(Field field, Object obj) throws IllegalAccessException {
        Object value = field.get(obj);
        if (value != null) {
            String name = (String) value;
            String result = StringUtils.left(name, 1);
            if (name.length() <= 2) {
                result += '*';
            } else {
                for (int i = 0; i < name.length() - 2; i++) {
                    result += '*';
                }
                result += StringUtils.right(name, 1);
            }
            field.setAccessible(true);
            field.set(obj, result);
        }
    }
}
